#pragma once

#include <stdint.h>
#include "raylib.h"
#include "list.h"

enum MaterialType
{
	DIFFUSE_AND_GLOSSY,
	REFLECTION_AND_REFRACTION,
	REFLECTION
};

struct Object;

/* BVH related start */

enum SplitMethod
{
	NAIVE,
	SAH
};

//two points to specify the bounding box
struct Bounds3
{
	Vector3 pMin, pMax;
};

//name conflict with raylib.h, Material to material
struct material
{
    enum MaterialType m_type;
    Vector3 m_color;
    Vector3 m_emission;
    float ior;
    float Kd, Ks;
    float specularExponent;
    //Texture tex;
};

enum splitAxis {
	AXIS_X = 0,
	AXIS_Y,
	AXIS_Z,
};

struct BVHBuildNode;
struct BVHBuildNode
{
    struct Bounds3 bounds;
    struct BVHBuildNode *left;
    struct BVHBuildNode *right;

	//save object point to array
	int* objAry; //itself
	int nPrimitives; //object number: left + right(child)

	int* leftAry; //left child
	int leftCnt; //object counter

	int* rightAry; //right child
	int rightCnt;

	struct list_head list; //linux list, for free nodes
	enum splitAxis splitAxis; //0 is x; 1 is y; 2 is z
};

struct BVHAccel
{
	struct BVHBuildNode* root;
	// BVHAccel Private Data
	int minPrims; //recursive end condition
    enum SplitMethod splitMethod;
};

/* BVH related end */

struct Light
{
	Vector3 position;
	Vector3 intensity;
};

//free memory
typedef void (*func_destroy_obj)(struct Object* obj);

//ray intersect with object
//input: origin and direction of ray; object pointer
//return:
//if hit, return true, and fill tnear and object index
//otherwise return false
typedef bool (*func_intersect)(struct Object* obj,
	const Vector3 orig, const Vector3 dir,
	float* tnear, uint32_t* index, Vector2* uv);

//get object normal and others
typedef void (*func_getSurfaceProperties)(struct Object* obj,
	const Vector3 P, const Vector3 dir, const uint32_t index,
	Vector2* uv, Vector3* N, Vector2* st);

//get diffuse color
typedef Vector3 (*func_evalDiffuseColor)(struct Object* obj, const Vector2 st);

struct Object
{
	enum MaterialType materialType;
	float ior; //Index Of Refraction
	float Kd, Ks;
	Vector3 diffuseColor;
	float specularExponent;

	func_intersect intersect;
	func_getSurfaceProperties getSurfaceProperties;
	func_evalDiffuseColor evalDiffuseColor;

	struct list_head list; //obj list
	//free obj resources
	func_destroy_obj destroy;
};

//class Sphere : public Object
struct Sphere
{
	struct Object obj; //must be first

	Vector3 center;
	float radius;
	float radius2;
};

//class Triangle : public Object
struct Triangle
{
	struct Object obj; //must be first

	Vector3 v0, v1, v2; // vertices A, B ,C , counter-clockwise order
	Vector3 center; //center of triangle
	Vector3 e1, e2;     // 2 edges v1-v0, v2-v0;
	Vector3 t0, t1, t2; // texture coords
	Vector3 normal;
	struct material m;

	struct Bounds3 box;
	int index; //gTriangle[index]
};

//class MeshTriangle : public Object
struct MeshTriangle
{
	struct Object obj; //must be first

	Vector3* vertices;
	uint32_t numTriangles;
	uint32_t* vertexIndex;
	Vector2*  stCoordinates;

	/* BVH related */
	struct list_head bvh_nodes; //head of BVH node list
    struct BVHAccel bvh;
};

//scene
struct Scene
{
	// setting up options
	int width;
	int height;
	double fov;
	Vector3 backgroundColor;
	int maxDepth;
	float bias;

	//creating the scene (adding objects and lights)
	struct list_head objects;
	struct Light* lights;

	//extra
	Vector3 eye_pos;
	Color* framebuffer;//rgba
};

struct hit_payload
{
    float tNear;
    uint32_t index;
    Vector2 uv;
    struct Object* hit_obj;
};

#define add_object(pObj, pscene) \
	list_add_tail(&pObj.obj.list, &pscene->objects);

extern void Render(struct Scene* scene);

extern bool default_intersect(struct Object* obj,
	const Vector3 orig, const Vector3 dir,
	float* tnear, uint32_t* index, Vector2* uv);

extern bool sphere_intersect(struct Object* obj,
	const Vector3 orig, const Vector3 dir,
	float* tnear, uint32_t* index, Vector2* uv);

extern bool rayTriangleIntersect(Vector3 v0, Vector3 v1,
	Vector3 v2, Vector3 orig,
	Vector3 dir, float* tnear, float* u, float* v);

extern bool triangle_intersect(struct Object* obj,
	const Vector3 orig, const Vector3 dir,
	float* tnear, uint32_t* index, Vector2* uv);

extern bool bvh_intersect(struct Object* obj,
	const Vector3 orig, const Vector3 dir,
	float* tnear, uint32_t* index, Vector2* uv);

//BVH related
extern struct Triangle *gTriangle; //head of triangles
extern void buildBVH(struct MeshTriangle *p);
extern void destroyBVH(struct MeshTriangle *p);

